#pragma once
#include <iostream>
#define _USE_MATH_DEFINES
#include <cmath>
// abstract base class
class EquationSolver
{
public:
    EquationSolver(double (*func)(double arg), int maxinum, double eps)
        : max_iter_num{maxinum}, epsilon{eps}, func_ptr{func}
    {
        iter_num = 0;
        result = 0;
        func_ptr = func;
    }

    virtual double solve() = 0; // pure virtual function

    void print();

    int iter_num;

protected:
    int max_iter_num;
    double epsilon;                 // make |f(result)|<epsilon
    double (*func_ptr)(double arg); // point to function f
    double result;                  // the solution of the equation
};

class BisectionMethod : public EquationSolver
{
public:
    BisectionMethod(double (*func)(double arg), double l = -INFINITY, double r = INFINITY, double d = 1.0E-8, int maxinum = 50, double eps = 1.0E-8)
        : EquationSolver{func, maxinum, eps}, left{l}, right{r}, delta{d}, len{0} {}

    /// @brief precondition:f ∈ C in [left,right],sgn(f(a)) ≠ sgn(f(b))
    /// @return numerical solution of the equation f(x) = 0
    double solve();

    double len;

private:
    double left, right; // left and right bound of the interval
    double delta;       // make right-left<delta
};

class NewtonMethod : public EquationSolver
{
public:
    NewtonMethod(double (*func)(double arg), double (*dfunc)(double arg), double x0, int maxinum = 50, double eps = 1.0E-8)
        : EquationSolver{func, maxinum, eps}, derivedfunc_ptr{dfunc}, x0{x0} {}

    /// @brief precondition: : f ∈ C2 and x0 is sufficiently close to a root of f
    /// @return numerical solution of the equation f(x) = 0
    double solve();

private:
    double x0;                             // initial value of iteration sequence
    double (*derivedfunc_ptr)(double arg); // point to derived function of f
};
class SecantMethod : public EquationSolver
{
public:
    SecantMethod(double (*func)(double arg), double x0, double x1, double d = 1.0E-8, int maxinum = 50, double eps = 1.0E-8)
        : EquationSolver{func, maxinum, eps}, x0{x0}, x1{x1}, delta{d} {}

    /// @brief precondition: : f ∈ C2 and x0,x1 are sufficiently close to a root of f
    /// @return numerical solution of the equation f(x) = 0
    double solve();

private:
    double x0, x1; // initial value and second value of iteration sequence
    double delta;  // make |xn-xn-1|<delta
};